<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
	<style type="text/css">
		* { 
			margin: 0;
			padding : 0;
			cursor: pointer;
		}
		body, html {
			height: 100%;
			overflow: hidden;
		}

		#instructionPrompt{
			position: relative;
			top: 40%;
			width: 100%;
			display: none;
			text-align: center;
			font-size: 5mm;
		}
	</style>
	<script type="text/javascript">
		/*
			For anyone reading this code, my apologies for any
			difficulties.  I lost my original version of this and had to
			decompile a compressed version that was cached on my browser to
			get the code you see here.
		*/
		var context, canvasWidth, canvasHeight;
		var mouseX = -1, mouseY = -1, mouseDown = false;
		var shape=[], numShapes = 0, colourAngle = 0, leadShape = null;
		var linkSize = 12, chainLength = 80, shrinkRate = .98;
		var colourOffset = { r : 0, g : 0 ,b : 0 };


		// a class representing the individual polygons
		var shapeClass = function(){
			this.colour = { 'R' : 255, 'G' : 255, 'B' : 255 };
			this.position = { x : 0, y : 0 };
			this.numpoints = this.radius = 0;
			this.points = [];
			this.velocity = { x : 0, y : 0 };
			this.opacity = 1;
			this.sine = this.cosine = 0;
			this.parentShape = null;
			this.childShape = null;
		};

		shapeClass.prototype.getTrigVals = function(ang){
			this.sine = Math.sin(ang);
			this.cosine = Math.cos(ang);
		};

		// animate the shape's motion
		shapeClass.prototype.move = function(){
			var n;
			if(this.parentShape == null){
				// no parent?  then go free!
				this.velocity.y++;
				this.position.x += this.velocity.x;
				this.position.y += this.velocity.y;
			}else{
				// trail behind our parent shape
				px = this.parentShape.position.x;
				py = this.parentShape.position.y;
				oldx = this.position.x;
				oldy = this.position.y;
				dx = px - oldx;
				dy = py - oldy;
				dist = Math.sqrt(dx * dx + dy * dy);
				dy *= linkSize / dist;
				dx *= linkSize / dist;
				this.position = {
					'x' : px - dx,
					'y' : py - dy
				};
				dx = this.position.x - oldx;
				dy = this.position.y - oldy;
				dx = dx > 5 ? 5 : (dx < -5 ? -5 : dx);
				dy = dy > 10 ?10 :(dy < -10 ? -10 : dy) - 3;
				this.velocity={
					x : dx,
					y : dy
				};
				
				for(n = 0; n < this.numpoints; n++){
					this.points[n].x *= shrinkRate;
					this.points[n].y *= shrinkRate;
				}
			}
			for(n = 0; n < this.numpoints; n++){
				var newx = this.cosine * this.points[n].x - this.sine * this.points[n].y;
				this.points[n].y = this.sine * this.points[n].x + this.cosine * this.points[n].y;
				this.points[n].x = newx;
			}
		};

		shapeClass.prototype.buildShape = function(x, y, radius, numpoints, colour){
			var n, ang, angi = Math.PI * 2 / numpoints, otherRadius;
			this.position={'x' : x, 'y' : y};
			this.numpoints = numpoints;
			this.radius = radius;
			this.ang = Math.random() * Math.PI * 2;
			this.angi = Math.random() * 0.2 + .01;
			this.points = [];
			this.colour = colour;
			otherRadius = radius * (1 + (!(this.numpoints % 2) && (this.numpoints > 8 || Math.random() < 0.5) ? 0.5 : 0));
			ang = this.ang;
			for(n = 0; n < this.numpoints; n++){
				if(n % 2){
					this.points[n] = {
						'x' : this.radius * Math.sin(ang),
						'y' : this.radius * Math.cos(ang)
					};
				}else{
					this.points[n] = {
						'x' : otherRadius * Math.sin(ang),
						'y' : otherRadius * Math.cos(ang)
					};
				}
				ang += angi;
			}
		};


		shapeClass.prototype.draw = function(){
			var n;

			context.save();
				context.beginPath();
				context.strokeStyle = 'rgba(' + (this.colour.R >> 1) + ', ' + (this.colour.G >> 1) + ', ' + (this.colour.B >> 1) + ', 1)';
				context.fillStyle = 'rgba(' + this.colour.R + ', ' + this.colour.G + ', ' + this.colour.B + ', ' + this.opacity + ')';
				context.lineWidth = 1;
				context.translate(this.position.x, this.position.y);
				context.moveTo(this.points[0].x, this.points[0].y);

				for(n = 1; n < this.numpoints; n++){
					context.lineTo(
						this.points[n].x, 
						this.points[n].y
					);
				}
				context.closePath();
				context.stroke();
				context.fill();
			context.restore();

			// let's flash the screen with the complement of that colour if the mouse button is down
			if(mouseDown){
				document.body.style.backgroundColor = 'rgba(' + (511 - this.colour.R >> 1) + ', ' + (511 - this.colour.G >> 1) + ', ' + (511 - this.colour.B >> 1) + ', 1)';
			}else{
				document.body.style.backgroundColor = '#FFFFFF';
			}
		};

		shapeClass.prototype.scale = function(ratio){
			var n;
			for(n = 0; n < this.numpoints; n++){
				this.points[n].x *= ratio;
				this.points[n].y *= ratio;
			}
		};

		// all things must die
		shapeClass.prototype.age = function(chainPos){
			chainPos = chainPos == undefined ? 0 : chainPos + 1;
			if(chainPos > chainLength){
				
				this.parentShape.childShape = null;
				this.parentShape = null;
				if(this.childShape != null){
					this.childShape.age(chainPos);
				}
			}else{
				if(this.childShape == null){
					if(this == leadShape){
						leadShape = null;
					}else{
						this.parentShape.childShape = null;
						this.parentShape = null;
					}
				}else{
					this.childShape.age(chainPos);
				}
			}
		};

		// generate the canvas and add it to the document
		function buildCanvas(){
			var canvas = document.createElement("canvas");
			canvasWidth = canvas.width = document.body.clientWidth;
			canvasHeight = canvas.height = document.body.clientHeight;
			canvas.id = 'gameCanvas';
			document.body.appendChild(canvas);
			return canvas;
		}

		// fade an element to the target opacity
		function fadeTo(element, target, callback){
			var opacity = parseFloat(element.style.opacity);
			var difference = target - opacity;
			var diff = Math.abs(difference);
			if(diff > .1){
				opacity += .1 * Math.sign(difference);
			}else{
				opacity = target;
				if(callback != undefined){
					callback();
				}
			}
			element.style.opacity = opacity;

			if(opacity != target){
				setTimeout(function(){fadeTo(element, target, callback);}, 50);
			}
		}

		function givePrompt(step){
			if(mouseX != -1 ||  mouseY != -1) return;

			var promptWin = document.getElementById('instructionPrompt');
			if(step == undefined){
				step = 0;
				promptWin.style.display = 'inline-block';
				promptWin.style.opacity = 0.1;
				setTimeout(function(){fadeTo(promptWin, 1);}, 50);
			}

			setTimeout(function(){
				fadeTo(promptWin, 0, function(){
					promptWin.style.display = 'none';
				});
			}, 3000);
		}

		function animate(){
			var n;
			context.clearRect(0, 0, canvasWidth, canvasHeight);
			for(n = 0; n < numShapes; n++){
				if(mouseDown){
					shape[n].scale(1.03);
				}
				shape[n].move();
				// that isNAN is a catch for something breaking the numbers.  it was leaving dead polygons at 0 0 sometimes
				if(isNaN(shape[n].position.y) || shape[n].position.y > canvasHeight + shape[n].radius){
					shape.splice(n, 1);
					n--;
					numShapes--;
				}else{
					shape[n].opacity *= shrinkRate;
					shape[n].draw();
				}
			}
			if(leadShape != null){
				leadShape.age();
			}
		}

		window.onload = function(){
			var canvas = buildCanvas();
			context = canvas.getContext('2d');
			canvas.onmousemove = function(evt){				
				var x, y, colour;
				x = evt.clientX;
				y = evt.clientY;
				var dx = x - mouseX, dy = y - mouseY;
				colour = {
					'R' : Math.floor(128 + 100 * Math.sin(colourAngle + colourOffset.r)),
					'G' : Math.floor(128 + 100 * Math.cos(colourAngle + colourOffset.g)),
					'B' : Math.floor(128 - 100 * Math.sin(colourAngle + colourOffset.b))
				};
				if(dx * dx + dy * dy > 256){
					shape[numShapes] = new shapeClass();
					shape[numShapes].buildShape(x, y, 40, Math.floor(Math.random() * 10) + 3, colour);
					dx = dx > 5 ? 5 : (dx < -5 ? -5 : dx);
					dy = dy > 10 ? 10 : (dy < -10 ? -10 : dy) - 3;
					shape[numShapes].getTrigVals(dx / 30.0);
					mouseX = x;
					mouseY = y;
					if(!mouseDown){
						shape[numShapes].velocity = {x : dx, y : dy};
						if(leadShape != null){
							leadShape.parentShape = shape[numShapes];
						}
						shape[numShapes].childShape = leadShape;
						leadShape = shape[numShapes];
					}else{
						shape[numShapes].velocity = {x : 6 * dx - Math.random() * 12 * dx, y : dy - 12};
					}
					numShapes++;
					colourAngle += 0.05;
					if(colourAngle > 2 * Math.PI) colourAngle -= 2 * Math.PI;
				}
			};

			document.onmouseup = function(){
				mouseDown = false;
				colourOffset = {
					r : 2 * Math.random() - 1,
					g : 2 * Math.random() - 1,
					b : 2 * Math.random() - 1
				};
				if(leadShape != null){
					leadShape.age(10);
				}
			};

			document.onmousedown = function(){
				mouseDown = true;
			};

			setInterval(animate, 40);

			setTimeout(givePrompt, 3001);
		};

	</script>
</head>
<body>
	<div id="instructionPrompt">
		Try using your mouse
	</div>
</body>
</html>
